package user

import (
	"context"
	"database/sql"
	"gf-demo/internal/dao"
	"gf-demo/internal/model"
	"gf-demo/internal/model/do"
	"gf-demo/internal/model/entity"
	"gf-demo/internal/service"
	"gf-demo/library/libUtils"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/errors/gcode"
	"github.com/gogf/gf/v2/errors/gerror"
)

type (
	sUser struct{}
)

func init() {
	service.RegisterUser(New())
}

func New() service.IUser {
	return &sUser{}
}

func (s *sUser) Create(ctx context.Context, in model.UserCreateInput) (res sql.Result, err error) {
	//临时变量
	var (
		available bool      //查重Name
		createBy  int64 = 0 //创建人id（0-系统）
	)
	//判断是注册还是新增用户
	isSignedIn := s.IsSignedIn(ctx)
	if isSignedIn {
		createBy = service.Session().GetUser(ctx).Id
	}
	// Name checks.
	available, err = s.IsNameAvailable(ctx, in.Name)
	if err != nil {
		return nil, err
	}
	if !available {
		column, _ := service.Locale().T(ctx, `table_sysUser_name`)
		str, _ := service.Locale().Tf(ctx, `used_column`, column, in.Name)
		return nil, gerror.NewCode(gcode.CodeBusinessValidationFailed, str)
	}
	id, err := s.Count(ctx)
	if err != nil {
		return nil, err
	}
	res, err = dao.SysUser.Ctx(ctx).Data(do.SysUser{
		Id:       id + 1,
		Name:     in.Name,
		Pwd:      libUtils.EncryptPassword(in.Name, in.Password),
		CreateBy: createBy,
		UpdateBy: createBy,
	}).Insert()
	if err != nil {
		return nil, err
	}
	affectRows, err := res.RowsAffected()
	if err != nil {
		return nil, err
	}
	str, _ := service.Locale().Tf(ctx, `rows_affected`, affectRows)
	return res, gerror.NewCode(gcode.CodeOK, str)
}

func (s *sUser) Delete(ctx context.Context, in model.UserDeleteInput) (sql.Result, error) {
	var (
		res sql.Result
	)
	// Id checks.
	available, err := s.IsIdAvailable(ctx, in.Id)
	if err != nil {
		return nil, err
	}
	if available {
		column, _ := service.Locale().T(ctx, `table_sysUser_id`)
		str, _ := service.Locale().Tf(ctx, `not_exist_column_v_int`, column, in.Id)
		return nil, gerror.NewCode(gcode.CodeBusinessValidationFailed, str)
	}
	res, err = dao.SysUser.Ctx(ctx).Delete(dao.SysUser.Columns().Id, in.Id)
	if err != nil {
		return nil, err
	}
	affectRows, err := res.RowsAffected()
	if err != nil {
		return nil, err
	}
	str, _ := service.Locale().Tf(ctx, `rows_affected`, affectRows)
	return res, gerror.NewCode(gcode.CodeOK, str)
}

func (s *sUser) Count(ctx context.Context) (int, error) {
	return dao.SysUser.Ctx(ctx).Data(do.SysUser{}).Count(dao.SysUser.Columns().Id)
}

func (s *sUser) Update(ctx context.Context, in model.UserUpdateInput) (sql.Result, error) {
	//临时变量
	var (
		res      sql.Result // sql执行结果
		updateBy int64      // 修改人id
	)
	updateBy = service.Session().GetUser(ctx).Id
	// Id checks.
	available, err := s.IsIdAvailable(ctx, in.Id)
	if err != nil {
		return nil, err
	}
	if available {
		//处理返回结果
		column, _ := service.Locale().T(ctx, `table_sysUser_id`)
		str, _ := service.Locale().Tf(ctx, `not_exist_column_v_int`, column, in.Id)
		return nil, gerror.NewCode(gcode.CodeBusinessValidationFailed, str)
	}
	res, err = dao.SysUser.Ctx(ctx).Data(do.SysUser{
		Name:     in.Name,
		Pwd:      libUtils.EncryptPassword(in.Name, in.Password),
		UpdateBy: updateBy,
	}).Where(do.SysUser{Id: in.Id}).Update()
	if err != nil {
		return nil, err
	}
	affectRows, err := res.RowsAffected()
	if err != nil {
		return nil, err
	}
	//处理返回结果
	str, _ := service.Locale().Tf(ctx, `rows_affected`, affectRows)

	return res, gerror.NewCode(gcode.CodeOK, str)
}

func (s *sUser) GetList(ctx context.Context) (gdb.Result, error) {
	res, err := dao.SysUser.Ctx(ctx).Data(do.SysUser{}).FieldsEx(dao.SysUser.Columns().Pwd).Order("id asc").All()
	if err != nil {
		return nil, err
	}
	str, _ := service.Locale().T(ctx, `request_success`)
	return res, gerror.NewCode(gcode.CodeOK, str)
}

func (s *sUser) Get(ctx context.Context, in model.UserGetInput) (gdb.Result, error) {
	if in.Name == "" {
		return s.GetList(ctx)
	}
	res, err := dao.SysUser.Ctx(ctx).FieldsEx(dao.SysUser.Columns().Pwd).Order("id asc").All("Name like ?", in.Name)
	if err != nil {
		return nil, err
	}
	str, _ := service.Locale().T(ctx, `request_success`)
	return res, gerror.NewCode(gcode.CodeOK, str)
}

// IsSignedIn checks and returns whether current user is already signed-in.
func (s *sUser) IsSignedIn(ctx context.Context) bool {
	if v := service.BizCtx().Get(ctx); v != nil && v.User != nil {
		return true
	}
	return false
}

// SignIn creates session for given user account.
func (s *sUser) SignIn(ctx context.Context, in model.UserSignInInput) error {
	var user *entity.SysUser
	err := dao.SysUser.Ctx(ctx).Where(do.SysUser{
		Name: in.Name,
		Pwd:  libUtils.EncryptPassword(in.Name, in.Password),
	}).Scan(&user)
	if err != nil {
		return err
	}
	str, _ := service.Locale().T(ctx, `login_error`)
	if user == nil {
		return gerror.NewCode(gcode.CodeOperationFailed, str)
	}
	if err = service.Session().SetUser(ctx, user); err != nil {
		return err
	}
	service.BizCtx().SetUser(ctx, &model.ContextUser{
		Id:   user.Id,
		Name: user.Name,
	})
	str, _ = service.Locale().T(ctx, `login_success`)
	return gerror.NewCode(gcode.CodeOK, str)
}

// SignOut removes the session for current signed-in user.
func (s *sUser) SignOut(ctx context.Context) error {
	return service.Session().RemoveUser(ctx)
}

// GetProfile retrieves and returns current user info in session.
func (s *sUser) GetProfile(ctx context.Context) *entity.SysUser {
	return service.Session().GetUser(ctx)
}

// IsNameAvailable checks and returns given Name is available for signing up.
func (s *sUser) IsNameAvailable(ctx context.Context, Name string) (bool, error) {
	count, err := dao.SysUser.Ctx(ctx).Where(do.SysUser{
		Name: Name,
	}).Count()
	if err != nil {
		return false, err
	}
	return count == 0, nil
}

// IsIdAvailable checks and returns given id is available.
func (s *sUser) IsIdAvailable(ctx context.Context, id int) (bool, error) {
	count, err := dao.SysUser.Ctx(ctx).Where(do.SysUser{
		Id: id,
	}).Count()
	if err != nil {
		return false, err
	}
	return count == 0, nil
}
